#include "IOmanager.h"
#include "fiber.h"
#include "macro.h"
#include "log.h"

#include <fcntl.h>
#include <sys/epoll.h>
#include <string.h>
#include <unistd.h>

namespace framework
{
static framework::Logger::ptr g_logger = FRAMEWORK_LOG_NAME("system");

std::string opToString(int op)
{
    switch (op)
    {
#define TOSTRING(x)      \
    case x: return #x;   \

    TOSTRING(EPOLL_CTL_ADD);
    TOSTRING(EPOLL_CTL_DEL);
    TOSTRING(EPOLL_CTL_MOD);

#undef TOSTRING
    default:
        break;
    }

    return "no find op";
}




static std::ostream& operator<< (std::ostream& os, EPOLL_EVENTS events)
{
    if(!events)
    {
        return os << "0";
    }

    bool first = true;
#define XX(E)               \
    if(events & E)          \
    {                       \
        if(!first)          \
        {                   \
            os << " | ";    \
        }                   \
        os << #E;           \
        first = false;      \
    }

    XX(EPOLLIN);        //关联的文件描述符可进行读取操作
    XX(EPOLLPRI);       //文件描述符上有异常条件。
    XX(EPOLLOUT);       //关联的文件描述符可进行写入操作
    XX(EPOLLRDNORM);    //普通数据可读取
    XX(EPOLLRDBAND);    //优先数据可读取
    XX(EPOLLWRNORM);    //普通数据可写入
    XX(EPOLLWRBAND);    //优先数据可写入
    XX(EPOLLMSG);       //未使用
    XX(EPOLLERR);       //关联的文件描述符上发生了错误条件。当管道的读取端被关闭时，此事件也会为管道的写入端报告。
//epoll_wait(2) 总是会报告此事件；在调用 epoll_ctl() 时，不必在事件中设置它1。
    XX(EPOLLHUP);       //关联的文件描述符上发生了挂起。epoll_wait() 总是会等待此事件；在调用 epoll_ctl() 时，不必在事件中设置它。
//注意，当从管道或流套接字等通道读取时，此事件仅表示对等方关闭了通道的一端。只有在通道中的所有未完成数据都被消耗完后，从通道读取才会返回 0（文件结束）。
    XX(EPOLLRDHUP);     //流套接字对等方关闭连接，或关闭连接的写入一半（自 Linux 2.6.17 起）。此标志对于编写简单的代码以检测使用边缘触发监视时对等方关闭非常有用。
    XX(EPOLLONESHOT);   //（自 Linux 2.6.2 起）：设置此标志后，当使用 epoll_wait() 收到事件时，将禁用与其关联的文件描述符。要启用文件描述符，请使用带有 EPOLL_CTL_MOD 的 epoll_ctl()。
    XX(EPOLLET);        //设置边缘触发行为；默认情况下，epoll 是电平触发的。
#undef XX

    return os;
}




//in 线程池默认线程数量
//in 是否使用当前调用线程(是否将调用线程包含进去)
//in 协程调度器名称
IOManager::IOManager(size_t threads, bool use_caller, const std::string& name)
    :Scheduler(threads, use_caller, name)
{
    //创建红黑树
    m_epfd = epoll_create(5000);
    FRAMEWORK_ASSERT(m_epfd > 0);

    //创建无名管道
    int rt = pipe(m_tickleFds);
    FRAMEWORK_ASSERT(!rt);

    //设置监听管道事件
    epoll_event event;
    memset(&event, 0, sizeof(epoll_event));
    event.events = EPOLLIN | EPOLLET;  //监听读事件，边沿触发
    event.data.fd = m_tickleFds[0];    //监听读端

    //将管道读端设置为非阻塞状态
    rt = fcntl(m_tickleFds[0], F_SETFL, O_NONBLOCK);
    FRAMEWORK_ASSERT(!rt);

    //将事件添加到红黑树上
    rt = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_tickleFds[0], &event);
    FRAMEWORK_ASSERT(!rt);

    //设置socket句柄上下文容器的大小
    contextResize(32);

    //启动协程调度器
    start();
}

IOManager::~IOManager()
{
    stop(); //停止协程调度器
    close(m_epfd);
    close(m_tickleFds[0]);
    close(m_tickleFds[1]);

    //释放内存
    for(size_t i = 0; i < m_fdContexts.size(); ++i)
    {
        if(m_fdContexts[i])
        {
            delete m_fdContexts[i];
        }
    }
}


//添加事件
//in socket句柄
//in 事件类型
//in 事件回调函数
//return 成功返回0，失败返回-1
int IOManager::addEvent(int fd, Event event, std::function<void()> cb)
{
    FDContext* fd_ctx = nullptr;
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size() > fd)
    {
        fd_ctx = m_fdContexts[fd];
        lock.unlock();
    }
    else
    {
        lock.unlock();
        RWMutex::WriteLock lock2(m_mutex);
        contextResize(fd * 1.5);
        fd_ctx = m_fdContexts[fd];
    }

    FDContext::MutexType::Lock lock3(fd_ctx->mutex);
    //如果连续两次加同一个事件（都是读事件或都是写事件，这样可能有两个线程在操作同一个方法）
    if(fd_ctx->events & event)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "addEvent assert fd =" << fd   
            << " event= " << (EPOLL_EVENTS)event 
            << " fd_ctx.event = " << (EPOLL_EVENTS)fd_ctx->events;
        FRAMEWORK_ASSERT(!(fd_ctx->events & event));
    }

    //添加事件
    int op = fd_ctx->events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
    epoll_event epevent;
    epevent.events = EPOLLET | fd_ctx->events | event;
    epevent.data.ptr = fd_ctx;
    int rt = epoll_ctl(m_epfd, op, fd, &epevent);
    if(rt)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ", "
            << opToString(op) << ", " << fd << ", " 
            << (EPOLL_EVENTS)epevent.events << "):"
            << rt << " (" << errno << ") (" << strerror(errno) << ") fd_ctx->events = "
            << (EPOLL_EVENTS)fd_ctx->events;
        return -1;
    }

    ++m_pendingEventCount;//当前等待执行的事件数量
    fd_ctx->events = (Event)(fd_ctx->events | event);
    FDContext::EventContext& event_ctx = fd_ctx->getContex(event);
    //断言全部为空
    FRAMEWORK_ASSERT(!event_ctx.scheduler
                    && !event_ctx.fiber
                    && !event_ctx.cb);
    
    //获取协程调度器的指针
    event_ctx.scheduler = Scheduler::GetThis();
    if(cb)
    {
        event_ctx.cb.swap(cb);
    }
    else
    {
        event_ctx.fiber = Fiber::GetThis();
        //断言当前协程为运行状态
        FRAMEWORK_ASSERT2(event_ctx.fiber->getState() == Fiber::EXEC,
                            "state = " << event_ctx.fiber->getState());
    }

    return 0;
}

//删除事件（不会触发事件）
//in socket句柄
//in 事件类型
bool IOManager::delEvent(int fd, Event event)
{
    //取出要删除的事件
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size() <= fd)
    {
        return false;
    }
    FDContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FDContext::MutexType::Lock lock2(fd_ctx->mutex);
    //如果 fd_ctx->events 中没有 event
    if(!(fd_ctx->events & event))
    {
        return false;
    }

    //从fd_ctx->events中去除event事件
    Event new_events = (Event)(fd_ctx->events & ~event);
    int op = new_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
    epoll_event epevent;
    epevent.events = EPOLLET | new_events;
    epevent.data.ptr = fd_ctx;

    int ret = epoll_ctl(m_epfd, op, fd, &epevent);
    if(ret)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd 
            << ", " << opToString(op)
            << ", " << fd << (EPOLL_EVENTS)epevent.events << "):"
            << ret << " (" << errno << ") (" << strerror(errno) << ")";
        
        return false;
    }

    --m_pendingEventCount;
    fd_ctx->events = new_events;
    //获取删除的那个事件
    FDContext::EventContext& event_ctx = fd_ctx->getContex(event);

    //将事件上下文类清空
    fd_ctx->resetContext(event_ctx);

    return true;
}

//取消事件（如果事件存在则触发事件）
//in socket句柄
//in 事件类型
bool IOManager::cancelEvent(int fd, Event event)
{
    //取出要取消的事件
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size() <= fd)
    {
        return false;
    }
    FDContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FDContext::MutexType::Lock lock2(fd_ctx->mutex);
    //如果 fd_ctx->events 中没有 event
    if(!(fd_ctx->events & event))
    {
        return false;
    }

    //从fd_ctx->events中去除event事件
    Event new_enents = (Event)(fd_ctx->events & ~event);
    int op = new_enents ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
    epoll_event epevent;
    epevent.events = EPOLLET | new_enents;
    epevent.data.ptr = fd_ctx;

    int ret = epoll_ctl(m_epfd, op, fd, &epevent);
    if(ret)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd 
            << ", " << opToString(op)
            << ", " << fd << (EPOLL_EVENTS)epevent.events << "):"
            << ret << " (" << errno << ") (" << strerror(errno) << ")";
        
        return false;
    }

    fd_ctx->triggerEvent(event);
    --m_pendingEventCount;

    return true;
}

//取消fd上的所有事件
//in socket句柄
bool IOManager::cancelAll(int fd)
{
    //取出要取消的事件
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size() <= fd)
    {
        return false;
    }
    FDContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FDContext::MutexType::Lock lock2(fd_ctx->mutex);
    //如果为空事件
    if(!fd_ctx->events) {
        return false;
    }

    int op = EPOLL_CTL_DEL;
    epoll_event epevent;
    epevent.events = 0;
    epevent.data.ptr = fd_ctx;

    int ret = epoll_ctl(m_epfd, op, fd, &epevent);
    if(ret)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd 
            << ", " << opToString(op)
            << ", " << fd << (EPOLL_EVENTS)epevent.events << "):"
            << ret << " (" << errno << ") (" << strerror(errno) << ")";
        
        return false;
    }

    if(fd_ctx->events & READ)
    {
        fd_ctx->triggerEvent(READ);
        --m_pendingEventCount;
    }

    if(fd_ctx->events & WRITE)
    {
        fd_ctx->triggerEvent(WRITE);
        --m_pendingEventCount;
    }

    FRAMEWORK_ASSERT(fd_ctx->events == 0);
    return true;
}

//返回当前的IOManager的调度器
IOManager* IOManager::GetThis()
{
    //将父类指针转换为子类指针（发生了继承，其实子类指针和父类指针相同）
    return dynamic_cast<IOManager*>(Scheduler::GetThis());
}

//通知协程调度器有任务了
void IOManager::tickle()
{
    //如果线程池中没有空闲线程
    if(!hasIdleThreads())
    {
        return;
    }

    //向管道中写入数据
    int rt = write(m_tickleFds[1], "T", 1);
    FRAMEWORK_ASSERT(rt == 1);
}

//返回是否可以停止
bool IOManager::stopping()
{
    uint64_t timeout = 0;
    return stopping(timeout);
}

//协程无任务时可调度执行idle协程
void IOManager::idle()
{
    FRAMEWORK_LOG_DEBUG(g_logger) << "IOManager net idle";
    const uint64_t MAX_EVNETS = 256;

    epoll_event* events = new epoll_event[MAX_EVNETS];
    //C++14之后就不用指定删除器了
    std::shared_ptr<epoll_event> shared_events(events, [](epoll_event* ptr){
        delete[] ptr;
    });

    while(true)
    {
        uint64_t next_timeout = 0;  
        //是否可以停止
        if(stopping(next_timeout)) //返回最近一个定时器执行的时间间隔（毫秒）
        {
            FRAMEWORK_LOG_INFO(g_logger) << "name = " << getName()
                                             << " idle stopping exit";
            break;
        }

        int rt = 0;
        do{
            static const int MAX_TIMEOUT = 3000;
            if(next_timeout != ~0ull)
            {
                //取小值
                next_timeout = (int)next_timeout > MAX_TIMEOUT ? MAX_TIMEOUT : next_timeout;
            }
            else
            {
                next_timeout = MAX_TIMEOUT;
            }

            rt = epoll_wait(m_epfd, events, MAX_EVNETS, (int)next_timeout);
           
            //忽略系统调用中断
            if(rt < 0 && errno == EINTR){
            }else{
                break;
            }

        }while(true);

        //获取需要执行的定时器回调函数列表
        std::vector<std::function<void()> > cbs;
        listExpiredCb(cbs);
        if(!cbs.empty())
        {
            //添加到协程调度器
            schedule(cbs.begin(), cbs.end());
            cbs.clear();
        }

        for(int i = 0; i < rt; ++i)
        {
            epoll_event& event = events[i];
            //如果是管道读端有数据
            if(event.data.fd == m_tickleFds[0])
            {
                //将所有数据读空
                uint8_t dummy[256];
                while(read(m_tickleFds[0], dummy, sizeof(dummy)) > 0);
                continue;
            }

            FDContext* fd_ctx = (FDContext*)event.data.ptr;
            FDContext::MutexType::Lock lock(fd_ctx->mutex);

            //对应的文件描述符发生错误或者文件描述符被挂断
            if(event.events & (EPOLLERR | EPOLLHUP)) 
            {
                event.events |= (EPOLLIN | EPOLLOUT) & fd_ctx->events;
            }

            int real_events = NONE;
            if(event.events & EPOLLIN)
            {
                real_events |= READ;
            }
            if(event.events & EPOLLOUT)
            {
                real_events |= WRITE;
            }

            //如果上下文中没有事件
            if((fd_ctx->events & real_events) == NONE)
            {
                continue;
            }

            //从上下文中去除要执行的事件
            int left_events = (fd_ctx->events & ~real_events);
            int op = left_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
            event.events = EPOLLET | left_events;

            //从新加回到树中
            int ret = epoll_ctl(m_epfd, op, fd_ctx->fd, &event);
            if(ret)
            {
                FRAMEWORK_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd 
                    << ", " << opToString(op)
                    << ", " << fd_ctx->fd << (EPOLL_EVENTS)event.events << "):"
                    << ret << " (" << errno << ") (" << strerror(errno) << ")";
                
                continue;
            }

            if(real_events & READ)
            {
                fd_ctx->triggerEvent(READ);
                --m_pendingEventCount;
            }
            if(real_events & WRITE)
            {
                fd_ctx->triggerEvent(WRITE);
                --m_pendingEventCount;
            }
        }

        //让出执行权限，走协程列表里的协程
        Fiber::ptr cur = Fiber::GetThis();
        auto raw_ptr = cur.get();
        cur.reset();

        raw_ptr->swapOut();
    }
}

//当有新的定时器插入到定时器的首部，执行该函数
void IOManager::onTimerInsertedAtFront()
{
    //唤醒epoll_wait,重新设置等待时间
    tickle();
}

//重置socket句柄上下文容器的大小
void IOManager::contextResize(size_t size)
{
    m_fdContexts.resize(size);
    
    for(size_t i = 0; i < m_fdContexts.size(); ++i)
    {
        if(!m_fdContexts[i])
        {
            m_fdContexts[i] = new FDContext;
            m_fdContexts[i]->fd = i;
        }
    }
}

//判断是否可以停止
//in 最近要出发的定时器事件间隔
bool IOManager::stopping(uint64_t& timeout)
{
    timeout = getNextTimer();
    return timeout == ~0ull          //ull后缀表示无符号长整形
        && m_pendingEventCount == 0  //当前等待执行的事件数量=0
        && Scheduler::stopping();    //协程调度器可以停止
}


//获取事件上下文
//in 事件类型
IOManager::FDContext::EventContext& IOManager::FDContext::getContex(Event event)
{
    switch (event)
    {
    case IOManager::READ:
        return read;
    case IOManager::WRITE:
        return write;
    default:
        FRAMEWORK_ASSERT2(false, "getContex");
    }

    //std::invalid_argument 函数参数无效时抛出这个异常
    throw std::invalid_argument("getContext invalid event");
}


//重置事件上下文
//[in, out]待重置的上下文类
void IOManager::FDContext::resetContext(EventContext& ctx)
{
    ctx.scheduler = nullptr;
    ctx.fiber.reset();
    ctx.cb = nullptr;
}

//触发事件
//in 事件类型
void IOManager::FDContext::triggerEvent(Event event)
{
    //断言当前事件中包含了要触发的事件
    FRAMEWORK_ASSERT(this->events & event);

    //从当前事件中去除要触发的事件
    this->events = (Event)(this->events & ~event);

    //获取要触发的事件类型
    EventContext& ctx = getContex(event);
    if(ctx.cb)
    {
        ctx.scheduler->schedule(&ctx.cb);
    }
    else
    {
        ctx.scheduler->schedule(&ctx.fiber);
    }
    ctx.scheduler = nullptr;
    return;
}

}
